
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Diagnostics;
using System.Xml;

using Swaf;
using Swaf.Container;
using Swaf.Security;

namespace Swaf.DataAccess
{
	/// <summary>
	/// This is a new implementation of the IDataRequest interface that can wrap any other
	/// IDataRequest implementation and provide decorator support for it.</summary>
	/// <remarks>
	/// This class uses the "DarDecorators" class factory to create all the decorators
	/// added to the DAR definition.
	/// 
	/// This class uses the "DataRequestTypes" class factory to create all the actual
	/// decorators that this wrapper is applied to.
	/// 
	/// This class assumes that when the INameAddressSupport.get or put is called
	/// with an IDictionary or INameAddressSupport, that the execute and format process
	/// should occur, which is the standard way all IDataRequest implementations should
	/// operate.  This class implements the get so that the preExecute, postExecute and
	/// postFormat methods of the IDarDecorator will be called properly.
	/// 
	/// This class expects all decorators to be applied to the root node and that
	/// the name of the decorators can be found in the "DarDecorators" factory.  Also,
	/// no decorator name should conflict with standard attributes of the DAR 
	/// being wrapped/decorated.  If there is an overlap, the value of the attribute
	/// needs to follow the standards of the wrapped/decorated DAR.  For example, the
	/// DbDataRequest DAR already has a connection attribute.  If a connection
	/// decorator is provided, it should allow its value to be that supported by 
	/// the DbDataRequest.  It is best to use names not support by any DAR already
	/// to avoid conflicts like that.</remarks>
	public class DecoratedDataRequest : IDataRequest, INameAddressSupport
	{
		protected XmlElement m_req;
		protected IDataRequest m_mainDataRequest;
		protected IList m_decorators;

		/// <summary>
		/// Processes all of the decorators found as attributes on the root element 
		/// of the catalog entry and then creates the wrapped/decorated dar by stripping
		/// "decorated" off the root element name and use the DataRequestTypes class
		/// factory.</summary>
		/// <param name="req">The XML catalog entry for DAR configuration.</param>
		/// <param name="nameInfo">The name used for the catalog.</param>
		public DecoratedDataRequest(XmlElement req, XmlCatalogContainerNode.NameInfo nameInfo)
		{
			m_req = req;
			string baseDataRequestName = req.Name;

			try
			{
				m_mainDataRequest = Application.globalApp.factories["DataRequestTypes"].
					createFromNickName(baseDataRequestName, req, nameInfo) as IDataRequest;
			}
			catch (Exception)
			{
				// Try the alternate constructor
				m_mainDataRequest = Application.globalApp.factories["DataRequestTypes"].
					createFromNickName(baseDataRequestName, req, nameInfo.CatalogLocationName + nameInfo.CatalogEntryName) as IDataRequest;
			}

			initialize();
		}

		/// <summary>
		/// Processes all of the decorators found as attributes on the root element 
		/// of the catalog entry and then creates the wrapped/decorated dar by stripping
		/// "decorated" off the root element name and use the DataRequestTypes class
		/// factory.</summary>
		/// <param name="req">The XML catalog entry for DAR configuration.</param>
		/// <param name="catalogName">The name used for the catalog.</param>
		public DecoratedDataRequest(XmlElement req, string catalogName)
		{
			m_req = req;
			string baseDataRequestName = req.Name;

			m_mainDataRequest = Application.globalApp.factories["DataRequestTypes"].
				createFromNickName(baseDataRequestName, req, catalogName) as IDataRequest;

			initialize();
		}

		private void initialize()
		{
			IClassFactory factory = Application.globalApp.factories["DarDecorators"];
			Debug.Assert(factory != null);
			foreach (XmlAttribute attr in m_req.Attributes)
			{
				if (factory.entries[attr.Name] != null)
				{
					if (m_decorators == null)
						m_decorators = new ArrayList();
					try
					{
						m_decorators.Add(factory.createFromNickName(attr.Name, attr.Name, attr.Value, m_req));
					}
					catch (DataAccessException e)
					{
						throw;
					}
					catch (Exception e)
					{
						throw new DataAccessException(
							string.Format("Error creating DAR decorator instance: '{0}'", attr.Name), e);
					}
				}
			}
		}

		#region IDataRequest Members
		/// <summary>
		/// Does the pre and post execute decorator calls and calls the inner DAR to do its
		/// work.</summary>
		public IDataResponse execute(Swaf.Container.INameAddressSupport info)
		{
			if (m_decorators != null)
				foreach (IDecorator deco in m_decorators)
					if (deco.IsStandin && deco is IDarDecorator)
						((IDarDecorator)deco).preExecute(ref info);

			IDataResponse rsp = m_mainDataRequest.execute(info);

			if (m_decorators != null)
				foreach (IDecorator deco in m_decorators)
					if (deco.IsStandin && deco is IDarDecorator)
						((IDarDecorator)deco).postExecute(ref info, ref rsp);

			return rsp;
		}

		/// <summary>
		/// Wrapps the IDictionary with an INameAddressSupport and calls
		/// this.execute(INameAddressSupport) version of the method.</summary>
		public IDataResponse execute(System.Collections.IDictionary info)
		{
			INameAddressSupport wrapperInfo = new FlexiMap(info, true);
			return execute(wrapperInfo);
		}
		#endregion

		#region INameAddressSupport Members
		public XmlSerializationSupport SerializationSupport
		{
			get { return XmlSerializationSupport.AsElement; }
		}

		public string Xml
		{
			get
			{
				return m_req.OuterXml;
			}
		}

		public bool containsField(string nameAddress)
		{
			return false;
		}

		public object get(string nameAddress, object oneArg)
		{
			return this.get(nameAddress, new object[] { oneArg });
		}

		public object get(string nameAddress)
		{
			return this.get(nameAddress, new object[] { });
		}

		/// <summary>
		/// The main implementation used by all others that will do the execute
		/// and format.  After data format for the DAR, the postExecute method of
		/// all standin decorators is executed.</summary>
		public object get(string nameAddress, params object[] infoArgs)
		{
			if (infoArgs == null || infoArgs.Length == 0)
				return this;
			else
			{
				INameAddressSupport info = infoArgs[0] as INameAddressSupport;
				if (info == null && infoArgs[0] is IDictionary)
					info = new FlexiMap(infoArgs[0] as IDictionary, true);

				if (info != null)
				{
					IDataResponse response;
					try
					{
						response = this.execute(info);
					}
					catch (Exception e)
					{
						throw;
					}

					object results = null;
					if (response != null)
						results = response.formatResults();

					if (m_decorators != null)
						foreach (IDecorator deco in m_decorators)
							if (deco.IsStandin && deco is IDarDecorator)
								((IDarDecorator)deco).postFormat(ref results);

					return results;
				}
				else
					throw new DataAccessException(
						string.Format("Invalid type, '{0}', passed for DAR call.  Needs to be IDictionary or INameAddressSupport",
						infoArgs[0].GetType().Name));
			}

			return null;
		}


		public string getXml(string options)
		{
			return m_req.OuterXml;
		}

		public bool isGetOnly
		{
			get { return ((INameAddressSupport)m_mainDataRequest).isGetOnly; }
		}

		public void put(string nameAddress, object val, object extraInfo)
		{
			get(nameAddress, val);
		}

		public void put(string nameAddress, object val)
		{
			get(nameAddress, val);
		}
		#endregion
		public IEnumerator GetEnumerator() { return null; }
	}

}
